package ai.easy.robot.logger

import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.runBlocking
import java.io.File
import java.util.*
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingQueue

/**
 * 全局的日志服务
 */
class LoggerService {
    companion object {

        val instance: LoggerService by lazy {
            LoggerService()
        }

        /**
         *
         */
        fun log(str: String, tag: String? = null) = instance.log(str, tag)

        /**
         * 打日志并推送到云端
         */
//        fun logAndPush(str: String,topic:String) = instance.logAndPush(str,topic)

        fun logAndPushBean(bean: Any, topic: String) = instance.logAndPushBean(bean, topic)

        /**
         * 日志初始化
         */
        fun init(initor: LoggerService.() -> Unit) {
            instance.initor()
            instance.ready = true
        }

        private const val TYPE_Normal = 0
        private const val TYPE_Push = 1

        private val DAY = 24L * 60 * 60 * 1000
    }

    private data class LogMsg(val str: String, var tagOrTopic: String? = null, var type: Int,
                              val time: Long = System.currentTimeMillis(), val bean: Any? = null) {}

    //日志消息队列
    private val msgQueue: BlockingQueue<LogMsg> = LinkedBlockingQueue()

    @Volatile
    private var looping: Boolean = true

    private var lastDay = 0L

    /**
     * 日志服务主循环,需要在单独线程中运行
     */
    fun mainLoop() {
        runBlocking {
            looping = true
            //为避免日志堵塞,每个放入队列,空闲时再取出打印
            lastDay = System.currentTimeMillis()
            while (looping) {
                if (msgQueue.size > 0) {
                    val msg = msgQueue.poll()
                    //
                    storeLog(msg)
                    if (msg.type == TYPE_Push) {
                        pusher?.invoke(msg.tagOrTopic ?: "/", msg.bean ?: msg.str)
                    }
                } else {
                    //
                    val now = System.currentTimeMillis()
                    if (now - lastDay > DAY) {
                        lastDay = now
                        clearBackup()
                    }
                    //避免cpu占有率过高
                    delay(50)
                    //yield()
                }
            }
            msgQueue.clear()
        }
    }

    private var ready = false

    fun stopLoop() {
        looping = false
    }

    /**
     * 清空备份的日志
     */
    fun clearBackup() {
        saveDirFile?.listFiles()?.filter { it.name.endsWith(".bak") }?.forEach {
            //大于7天的删除
            if (System.currentTimeMillis() - it.lastModified() > 7 * DAY) {
                it.delete()
            }
//            val len = it.name.length
//            val ftime = it.name.substring(len-4-13,len-4)
//            val _7dayago=timeFmt.format(Date(System.currentTimeMillis() - 7*DAY))
//            //
//            if(ftime<_7dayago){
//                it.delete()
//            }
        }
    }

    /**
     * 保存目录
     */
    var saveDir: String
        get() = saveDirFile?.absolutePath ?: ""
        set(value) {
            saveDirFile = File(value).apply {
                if (!exists()) {
                    mkdirs()
                }
            }
        }

    /**
     * 推送到云端
     */
    var pusher: ((String, Any) -> Unit)? = null

    //var beanPusher:((String,Any)->)

    private var saveDirFile: File? = null

    //private val timeFmt=SimpleDateFormat("yyMMdd-hhmmss")

    private fun storeLog(msg: LogMsg) {
        if (null == saveDirFile) {
            return
        }
        //文件名称
        val fn = when (msg.type) {
            TYPE_Push -> "Push_log"
            else -> "${msg.tagOrTopic ?: ""}_log"
        }

        val f = File(saveDirFile, "${fn}.log").apply {
            if (!exists()) {
                createNewFile()
            }
        }
        //
        synchronized(f) {
            //超过4M,转移日志文件
            if (f.length() > 4000000L) {
                f.copyTo(File(saveDirFile, "$fn-${Date(msg.time)}.bak"))
                f.writeText("")
            }
            //
            f.appendText("[${Date(msg.time).dateFormat()}] ${msg.str}\n")
        }
    }

    fun logAndPushBean(bean: Any, topic: String) {
        if (ready) {
            msgQueue.offer(LogMsg("", topic, TYPE_Push, bean = bean))
        }
    }

    @JvmOverloads
    fun log(str: String, tag: String? = null) {
        if (ready) {
            msgQueue.offer(LogMsg(str, tag, TYPE_Normal))
        }
    }
}